from datetime import datetime
import logging
from constants import UUID_NIL
from controllers.errors.online_mall.category_errors import HProdCategoryNotFoundError, HProdCategoryParamError
from controllers.errors.online_mall.shop_errors import HShopNotFoundError
from core.entities.account_role import AccountWrap
from extensions.ext_database import db
from werkzeug.exceptions import InternalServerError
from flask_babel import _
from models.online_mall.prod_category import MCategoryBrand, MCategoryProperty, MProdCategory
from services.file_service import FileService

class ProdCategoryService:
    @staticmethod
    def get_category_list(account:AccountWrap,args:dict)->dict:
        try:
            query = db.session.query(MProdCategory).filter(MProdCategory.tenant_id==account.current_tenant_id)
            total = 0
            has_more = False
            if 'id' in args and args['id']:
                query = query.filter(MProdCategory.id==args['id'])
                total = query.count()
            else:
                if 'shop_id' in args and args['shop_id']:
                    query = query.filter(MProdCategory.shop_id==args['shop_id'])
                if 'parent_id' in args and args['parent_id']:
                    query = query.filter(MProdCategory.parent_id==args['parent_id'])
                if 'name' in args and args['name']:
                    query = query.filter(MProdCategory.name.like('%'+args['name']+'%'))
                if 'status' in args and args['status'] is not None:
                    query = query.filter(MProdCategory.status==args['status'])
                total = query.count()
                if 'page' in args and 'page_size' in args and args['page'] > 0 and args['page_size'] > 0:
                    query = query.offset((args['page']-1)*args['page_size']).limit(args['page_size'])
                    has_more = total > args['page']*args['page_size']
            data = query.order_by(MProdCategory.seq.desc()).all()
            return {
                "total":total,
                "has_more":has_more,
                "data":data
            }
        except Exception as e:
            logging.error(e)
            return {
                "total":0,
                "has_more":False,
                "data":[],
                "error":str(e)
            }
        
    @staticmethod
    def _next_category_seq(tenant_id:str,parent_id:str|None)->int:
        if not parent_id:
            next_seq = db.session.query(MProdCategory.seq).filter(MProdCategory.tenant_id==tenant_id).order_by(MProdCategory.seq.desc()).scalar()
            return 1 if not next_seq else next_seq + 1
        else:
            next_seq = db.session.query(MProdCategory.seq).filter(MProdCategory.tenant_id==tenant_id,MProdCategory.parent_id==parent_id).order_by(MProdCategory.seq.desc()).first()
            return 1 if not next_seq else next_seq[0] + 1
    
    @staticmethod
    def create_category(account:AccountWrap,args:dict)->MProdCategory:
        if not account or not isinstance(account,AccountWrap):
            raise NotLoginError()
        
        category = MProdCategory(
            tenant_id = account.current_tenant_id, # type:ignore
            created_by = account.id, # type:ignore
        )
        ProdCategoryService._fill_category_params(account, args, category)
        try:
            db.session.add(category)
            db.session.commit()
            return category
        except Exception as e:
            db.session.rollback()
            raise InternalServerError(str(e))

    @staticmethod
    def update_category(account:AccountWrap,args:dict)->MProdCategory:
        if not account or not isinstance(account,AccountWrap):
            raise NotLoginError()
        category_id = args['id'] if 'id' in args else None
        if not category_id:
            raise HProdCategoryNotFoundError(category_id)
        category = db.session.get(MProdCategory,category_id)
        if not category:
            raise HProdCategoryNotFoundError(category_id)
        ProdCategoryService._fill_category_params(account, args, category)
        category.created_at = datetime.now()
        category.created_by = account.id
        try:
            db.session.add(category)
            db.session.commit()
            return category
        except Exception as e:
            db.session.rollback()
            raise InternalServerError(str(e))
    @staticmethod 
    def delete_category(account:AccountWrap,args:dict)->MProdCategory:
        if not account or not isinstance(account,AccountWrap):
            raise NotLoginError()
        category_id = args['id'] if 'id' in args else None
        if not category_id:
            raise HProdCategoryNotFoundError(category_id)
        category = db.session.get(MProdCategory,category_id)
        if not category:
            raise HProdCategoryNotFoundError(category_id)
        try:
            db.session.query(MProdCategory).filter(MProdCategory.parent_id==category.id).delete()
            db.session.query(MCategoryProperty).filter(MCategoryProperty.category_id==category.id).delete()
            db.session.query(MCategoryBrand).filter(MCategoryBrand.category_id==category.id).delete()
            db.session.delete(category)
            db.session.commit()
            return category
        except Exception as e:
            db.session.rollback()
            raise InternalServerError(str(e))
        
    @staticmethod
    def _fill_category_params(account, args, category):
        shop_id = args['shop_id'] if 'shop_id' in args and args['shop_id'] else None
        if not shop_id:
            raise HShopNotFoundError(shop_id)
        category.shop_id = shop_id

        parent_id = args['parent_id'] if 'parent_id' in args and args['parent_id'] else None
        if parent_id and parent_id != UUID_NIL:
            parent_category = db.session.get(MProdCategory,parent_id)
            if not parent_category:
                raise HProdCategoryNotFoundError(parent_id)
            category.parent_id = parent_id
            category.level = parent_category.level + 1
        else:
            category.level = 0
            category.parent_id = UUID_NIL
            
        category.parent_id = parent_id
        if 'seq' in args and args['seq']:
            category.seq = args['seq']
        else:
            next_seq = ProdCategoryService._next_category_seq(account.current_tenant_id,parent_id)
            category.seq = next_seq

        name = args['name'] if 'name' in args and args['name'] else None
        if not name:
            raise HProdCategoryParamError(_("Category name can not be empty."))
        category.name = name

        icon_file_id = args['icon_file_id'] if 'icon_file_id' in args and args['icon_file_id'] else None
        if icon_file_id:
            if not FileService.file_exist(icon_file_id):
                raise HProdCategoryParamError(_("Icon File does not exist."))
            category.icon_file_id = icon_file_id
        
        pic_file_id = args['pic_file_id'] if 'pic_file_id' in args and args['pic_file_id'] else None
        if pic_file_id:
            if not FileService.file_exist(pic_file_id):
                raise HProdCategoryParamError(_("Picture File does not exist."))
            category.pic_file_id = pic_file_id
